package org.example.gc;
/**
 * 测试对象的finalization机制
 * @author youshang
 * @date 2022/03/30 08:56
 **/
public class CanReliveObj {
    private static CanReliveObj obj;

    /**
     * finalize 方法只会被调用一次
     * finalize() 是对象逃脱死亡的最后机会，稍后 GC 会对 F-Queue队列中的对象进行第二次标记。
     * **如果 objA 在 finalize() 中与应用链上的任何一个对象建立了联系**，
     * 那么在第二次标记时， objA 会被移出 "即将回收" 集合。
     * 之后对象会再次出现没有引用存在的情况。在这个情况下 finaliz() 方法不会被再次调用,
     * 对象会直接变成不可触及的状态，也就是说，**一个对象的finalize() 只会被调用一次**
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("调用当前类重写的finalize方法");
        obj = this;//当前待回收的对象在finalize()方法中引用链上的一个对象 与 obj建立了联系
    }

    public static void main(String[] args) throws InterruptedException {
        obj = new CanReliveObj();
        obj = null;
        System.gc();
        System.out.println("第一次 GC");

        //因为Finalizer线程优先级很低，暂停2秒 等待一下
        Thread.sleep(2000);

        if (obj == null) {
            System.out.println("obj is dead");
        }else {
            System.out.println("obj is still alive");
        }
        System.out.println("第二次 GC");
        //下面这段代码上面的完全相同。但是这次自救却失败了
        obj = null;
        System.gc();
        //因为Finalizer线程优先级很低，暂停2秒 等待一下
        Thread.sleep(2000);
        if (obj == null) {
            System.out.println("obj is dead");
        }else {
            System.out.println("obj is still alive");
        }

        /*
        总结：
            1. 如果对象 objA 到 GC Roots没有引用链，则进行一次标记
            2. 进行筛选，判断此对象是否有必要执行 finalize()
                1. 如果对象 objA 重写了 finalize() 或者 finalize() 已经被虚拟机调用过，则虚拟机视为 "没有必要执行"，objA 被判定为不可触及的
                2. 如果对象 objA 重写了finalize() ,且还未执行过，那么objA 会被插入到 F-Queue 队列中，
                    由一个虚拟机自动创建的、低优先级的 **Finalizer** (通过jvisualvm线程可以查看一直有一个等待的线程) 线程触发其 finalize() 执行。
                3. finalize() 是对象逃脱死亡的最后机会，稍后 GC 会对 F-Queue队列中的对象进行第二次标记。
                    **如果 objA 在 finalize() 中与应用链上的任何一个对象建立了联系**，那么在第二次标记时，
                    objA 会被移出 "即将回收" 集合。之后对象会再次出现没有引用存在的情况。
                    在这个情况下 finaliz() 方法不会被再次调用,对象会直接变成不可触及的状态，也就是说，**一个对象的finalize() 只会被调用一次**
         */
    }
}
